﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using JsonKnownTypes.Exceptions;
using JsonKnownTypes.Utils;

namespace JsonKnownTypes
{
    public static class JsonKnownTypesSettingsManager
    {
        public static JsonDiscriminatorSettings DefaultDiscriminatorSettings { get; set; } = new JsonDiscriminatorSettings();

        private static HashSet<Assembly> RegisteredAssemblies = new HashSet<Assembly>();
        public static void RegisterTypeAssembly<T>()
        {
            var type = typeof(T);
            RegisteredAssemblies.Add(type.Assembly);
        }
        
        public static JsonKnownTypesSettings GetSettings<T>()
        {
            var discriminatorAttribute = AttributesManager.GetJsonDiscriminatorAttribute(typeof(T));
            
            var discriminatorSettings = discriminatorAttribute == null ? DefaultDiscriminatorSettings : Mapper.Map(discriminatorAttribute);
            
            var typeSettings = new JsonKnownTypesSettings
            {
                Name = discriminatorSettings.Name
            };

            var allTypes = GetAllInheritance<T>();

            typeSettings.TypeToDiscriminator.AddJsonKnown<T>();
            typeSettings.TypeToDiscriminator.AddJsonKnownThis(allTypes);
            
            if (discriminatorSettings.AutoJsonKnown)
            {
                typeSettings.TypeToDiscriminator.AddAutoGenerated(allTypes);
            }
            else if(!allTypes.All(typeSettings.TypeToDiscriminator.ContainsKey))
            {
                throw new JsonKnownTypesException("Dictionary contains not all types of hierarchy, enable AutoJsonKnow or add attributes for all classes"); //todo
            }

            typeSettings.DiscriminatorToType = typeSettings.TypeToDiscriminator.Revert();

            return typeSettings;
        }

        private static Type[] GetAllInheritance<T>()
        {
            RegisterTypeAssembly<T>();
            var type = typeof(T);
            return RegisteredAssemblies.SelectMany(a=>a.GetTypes())
                .Where(x => type.IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract)
                .ToArray();
        }
    }
}
